﻿using System;
using System.ComponentModel;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using SongSharp.Domain;
using SongSharp.Domain.Queueing;
using SongSharp.Domain.Objects;
using SongSharp.Domain.Playing;
using SongSharp.Domain.Events;
using SongSharp.Domain.Tagging;

namespace SongSharp.Appliance
{
    public class PlayerAppInfo : INotifyPropertyChanged
    {
        public const int MAX_CHAR_DISPLAY_LENGTH = 45;

        #region Properties
        private static PlayerAppInfo ms_instance;
        private PlayerAppInfo() { }

        public static PlayerAppInfo Instance
        {
            get
            {
                if (ms_instance == null)
                    ms_instance = new PlayerAppInfo();
                return ms_instance;
            }
        }

        public event PropertyChangedEventHandler PropertyChanged;
        public event TrackStartedEventHandler TrackStarted;

        public delegate void TrackStartedEventHandler(object sender, TrackStartedEventArgs e);

        private double m_trackPosition;
        private string m_currentTrackInfo;
        private DateTime m_time;
        private string m_log;

        public DateTime Time
        {
            get { return m_time; }
            set
            {
                m_time = value;
                OnPropertyChanged("Time");
            }
        }

        public double TrackPosition
        {
            get { return m_trackPosition; }
            set
            {
                m_trackPosition = value;
                OnPropertyChanged("TrackPosition");
            }
        }

        public string CurrentTrackInfo
        {
            get { return m_currentTrackInfo; }
            set
            {
                m_currentTrackInfo = value;
                OnPropertyChanged("CurrentTrackInfo");
            }
        }

        internal virtual void OnTrackStarted(Track track)
        {
            if (TrackStarted != null)
            {
                TrackStarted(this, new TrackStartedEventArgs(track));
            }
        }

        protected virtual void OnPropertyChanged(string propertyName)
        {
            if (PropertyChanged != null)
            {
                PropertyChanged(this,
                    new PropertyChangedEventArgs(propertyName));
            }
        }
        #endregion

        internal void UpdateTrackInfo(Track track)
        {
            if (track != null)
            {
                string infoString = "";
                TagInfo info = TagReader.Read(track);
                if (info.Title == null || info.Artist == null)
                    infoString = System.IO.Path.GetFileNameWithoutExtension(track.Url);
                else
                {
                    infoString = info.Title + " by " + info.Artist;
                    if (info.Album != null)
                        infoString += " (" + info.Album + ")";
                }
                if (infoString.Length > MAX_CHAR_DISPLAY_LENGTH)
                    infoString = infoString.Substring(0, MAX_CHAR_DISPLAY_LENGTH) + "...";
                CurrentTrackInfo = infoString;
            }
        }
    }

    public static class PlayerApp
    {
        public static PlaybackManager PlaybackManager = PlaybackManager.Instance;
        private static PlayerAppInfo PlayerInfo = PlayerAppInfo.Instance;

        public static Track CurrentTrack { get; private set; }
        internal static bool IsPaused { get; set; }

        static PlayerApp()
        {
            PlaybackManager.ReachedEndOfTrack += new ReachedEndOfTrackEventHandler(Basic.ReachedEndOfTrack);
            PlaybackManager.TrackPositionChanged += new TrackPositionChangedEventHandler(Basic.TrackPositionChanged);
        }

        public static class Basic
        {
            public static void PlayNext(TrackHandlingType handling)
            {
                switch (handling)
                {
                    case TrackHandlingType.SkippedTrack:
                        SuggestionApp.TrackSkipped(CurrentTrack);
                        break;
                }

                Track track = QueueApp.DeQueue();
                CurrentTrack = track;
                PlaybackManager.Play(track);
                PlayerInfo.UpdateTrackInfo(track);

                Clock.ResetTime();

                if (track != null)
                {
                    Clock.StartTimer();
                    PlayerInfo.OnTrackStarted(track);
                }
            }

            public static void PlayPrevious()
            {
                SuggestionApp.TrackSkipped(CurrentTrack);

                Track track = QueueApp.DeQueuePrevious();

                CurrentTrack = track;
                PlaybackManager.Play(track);
                PlayerInfo.UpdateTrackInfo(track);

                Clock.ResetTime();

                if (track != null)
                {
                    PlayerInfo.OnTrackStarted(track);
                }
            }

            public static void Pause()
            {
                if (IsPaused)
                    IsPaused = false;
                else
                    IsPaused = true;

                PlaybackManager.Pause();
            }

            public static void Seek(double percent)
            {
                PlaybackManager.Seek(percent);
            }

            public static void ReachedEndOfTrack(object sender, EventArgs e)
            {
                SuggestionApp.TrackPlayed(CurrentTrack);
                PlayNext(TrackHandlingType.PlayedTrackToEnd);
            }

            public static void TrackPositionChanged(object sender, TrackPositionChangedEventArgs e)
            {
                PlayerInfo.TrackPosition = e.NewPosition;
            }

            public static void VolumeChanged(object sender, double newVolume)
            {
                PlaybackManager.Volume = (int)newVolume;
                SongSharpApp.States.CurrentVolume = (int)newVolume;
            }
        }

        public static class Clock
        {
            private static BackgroundWorker ClockWorker { get; set; }

            static Clock()
            {
                ClockWorker = new BackgroundWorker();
                ClockWorker.DoWork += new DoWorkEventHandler(ClockWorker_DoWork);
            }

            private static void ClockWorker_DoWork(object sender, DoWorkEventArgs e)
            {
                while (true)
                {
                    if (!IsPaused)
                    {
                        PlayerInfo.Time = PlayerInfo.Time.AddSeconds(1.0);
                    }
                    Thread.Sleep(1000);
                }
            }

            internal static void StartTimer()
            {
                if (!ClockWorker.IsBusy)
                {
                    ClockWorker.RunWorkerAsync();
                }
            }

            internal static void ResetTime()
            {
                DateTime time = DateTime.MinValue;
                PlayerInfo.Time = time;
            }
        }
    }
}
